/*
 * symbol_table.c
 *
 *  Created on: Dec 12, 2010
 *      Author: cpy.prefers.you@gmail.com
 */

#include <stdlib.h>
#include <string.h>
#include "symbol_table.h"
#include "lexer.h"
#include "buildin_commands.h"
 
static SymbolList_T *HashTable[HASH_TABLE_SIZE];
static SymbolStack_T *LocalVariable;

static int Hash(char *Name);
static SymbolList_T *LookupNameInSymbolList(char *Name);

Symbol_T *NewSymbol()
{
    Symbol_T *temp = calloc(1, sizeof(Symbol_T));
    while (!temp)
        temp = calloc(1, sizeof(Symbol_T));
    return temp;
}

void CopySymbol(Symbol_T **Dest, Symbol_T *Source)
{
    if (!Source)
    {
        FreeSymbol(Dest);
        return;
    }
    if (!(*Dest))
        *Dest = NewSymbol();

    strncpy((*Dest)->Name, Source->Name, TOKEN_LEN);
    (*Dest)->Type = Source->Type;
    (*Dest)->FunctionPointer = Source->FunctionPointer;
    (*Dest)->Precedence = Source->Precedence;

    FreeSymbolList(&((*Dest)->Element));
    FreeSymbolList(&((*Dest)->ParameterList));
    FreeSymbolList(&((*Dest)->ProcedureBody));

    CopySymbolList(&((*Dest)->Element), Source->Element);
    CopySymbolList(&((*Dest)->ParameterList), Source->ParameterList);
    CopySymbolList(&((*Dest)->ProcedureBody), Source->ProcedureBody);

    FreeSymbol(&((*Dest)->Value));
    CopySymbol(&((*Dest)->Value), Source->Value);

    return;
}

void CopySymbolList(SymbolList_T **Dest, SymbolList_T *Source)
{
    if (!Source)
    {
        if (*Dest)
            FreeSymbolList(Dest);
        return;
    }
    if (!*Dest)
        *Dest = NewSymbolList();
    CopySymbol(&((*Dest)->Symbol), Source->Symbol);
    CopySymbolList(&((*Dest)->Next), Source->Next);
    return;
}

SymbolList_T *NewSymbolList()
{
    SymbolList_T *temp = calloc(1, sizeof(SymbolList_T));
    while (!temp)
       temp = calloc(1, sizeof(SymbolList_T));
    return temp;
}

int IsEmptyStack(SymbolStack_T *SymbolStack)
{
    return SymbolStack->SymbolList == 0;
}

void PopStack(SymbolStack_T *SymbolStack, Symbol_T *Symbol)
{
    SymbolList_T *Temp = SymbolStack->SymbolList;
    SymbolStack->SymbolList = SymbolStack->SymbolList->Next;
    CopySymbol(&Symbol, Temp->Symbol);
    Temp->Next = 0;
    FreeSymbolList(&Temp);
    return;
}

void PushStack(SymbolStack_T *SymbolStack, Symbol_T *Symbol)
{
    SymbolList_T *Temp = NewSymbolList();
    CopySymbol(&(Temp->Symbol), Symbol);
    Temp->Next = SymbolStack->SymbolList;
    SymbolStack->SymbolList = Temp;
    return;
}

SymbolStack_T *NewSymbolStack()
{
    SymbolStack_T *temp = calloc(1, sizeof(SymbolStack_T));
    while (!temp)
        temp = calloc(1, sizeof(SymbolStack_T));
    return temp;
}

void FreeSymbolStack(SymbolStack_T **SymbolStack)
{
    FreeSymbolList(&((*SymbolStack)->SymbolList));
    free(*SymbolStack);
    *SymbolStack = 0;
    return;
}

void InsertLocalVariable(Symbol_T *Symbol)
{
    PushStack(LocalVariable, Symbol);
    InsertSymbol(Symbol);
    return;
}

void RemoveLocalVariable(char *Name)
{
    Symbol_T *Temp = NewSymbol();
    PopStack(LocalVariable, Temp);
    RemoveSymbol(Name);
    FreeSymbol(&Temp);
    return;
}
void RemoveAllLocalVariable()
{
    Symbol_T *Temp = NewSymbol();
    while (!IsEmptyStack(LocalVariable))
    {
        PopStack(LocalVariable, Temp);
        RemoveSymbol(Temp->Name);
    }
    FreeSymbol(&Temp);
    return;
}

void FreeSymbol(Symbol_T **Symbol)
{
    if (!*Symbol)
        return;
    FreeSymbol(&((*Symbol)->Value));
    FreeSymbolList(&((*Symbol)->Element));
    FreeSymbolList(&((*Symbol)->ParameterList));
    FreeSymbolList(&((*Symbol)->ProcedureBody));
    free(*Symbol);
    *Symbol = 0;
    return;
}

void FreeSymbolList(SymbolList_T **SymbolList)
{
    if (!*SymbolList)
        return;
    FreeSymbolList(&((*SymbolList)->Next));
    FreeSymbol(&((*SymbolList)->Symbol));
    free(*SymbolList);
    *SymbolList = 0;
}

void UpdateValue(Symbol_T *Symbol)
{
    Symbol_T *temp = LookupName(Symbol->Name);
    if (temp)
        CopySymbol(&(temp->Value), Symbol->Value);
    else
        InsertSymbol(Symbol);
    return;
}

/* actually the well-known ELFHash */
static int Hash(char *Name)
{
    unsigned long HashValue = 0;
    unsigned long Symbol;
    while (*Name)
    {
        HashValue <<= 4;
        HashValue += *Name++;
        Symbol = HashValue & 0xf0000000l;
        if (Symbol)
            HashValue ^= Symbol >> 24;
        HashValue &= ~Symbol;
    }   
    return HashValue % HASH_TABLE_SIZE;
}

Symbol_T *InsertSymbol(Symbol_T *SymbolToInsert)
{
    Symbol_T *Symbol = NewSymbol();
    CopySymbol(&Symbol, SymbolToInsert);
    SymbolList_T *SymbolList = NewSymbolList();
    SymbolList->Symbol = Symbol;

    int HashKey = Hash(Symbol->Name);

    SymbolList->Next = HashTable[HashKey];
    HashTable[HashKey] = SymbolList;

    return Symbol;
}

static SymbolList_T *LookupNameInSymbolList(char *Name)
{
    int HashKey = Hash(Name);
    SymbolList_T *SymbolList = HashTable[HashKey];
    while (SymbolList)
    {
        if (!strcmp(Name, SymbolList->Symbol->Name))
            break;
        SymbolList = SymbolList->Next;
    }
    return SymbolList;
}

Symbol_T *LookupName(char *Name)
{
    SymbolList_T *SymbolList = LookupNameInSymbolList(Name);
    return SymbolList ? SymbolList->Symbol : NULL;
}

void RemoveSymbol(char *Name)
{
    int HashKey = Hash(Name);
    SymbolList_T **SymbolList = &HashTable[HashKey];
    while (*SymbolList)
    {
        if (!strcmp(Name, (*SymbolList)->Symbol->Name))
            break;
        *SymbolList = (*SymbolList)->Next;
    }

    if (*SymbolList)
    {
        FreeSymbol(&((*SymbolList)->Symbol));
        SymbolList_T *temp = *SymbolList;
        *SymbolList = (*SymbolList)->Next;
        free(temp);
    }

    return;
}

void SymbolTableInit()
{
    LocalVariable = NewSymbolStack();

    InsertSymbol(&(Symbol_T){ .Name = "(", .Type = TYPE_L_PARENTHESIS });
    InsertSymbol(&(Symbol_T){ .Name = ")", .Type = TYPE_R_PARENTHESIS });

    InsertSymbol(&(Symbol_T){ .Name = TRUE, .Type = TYPE_BOOLEAN_TRUE });
    InsertSymbol(&(Symbol_T){ .Name = FALSE, .Type = TYPE_BOOLEAN_FALSE });

    InsertFunction("and",          _and);
    InsertFunction("array",        _array);
    InsertFunction("arraytolist",  _arraytolist);
    InsertFunction("butfirst",     _butfirst);
    InsertFunction("butlast",      _butlast);
    InsertFunction("first",        _first);
    InsertFunction("if",           _if);
    InsertFunction("ifelse",       _ifelse);
    InsertFunction("item",         _item);
    InsertFunction("last",         _last);
    InsertFunction("list",         _list);
    InsertFunction("not",          _not);
    InsertFunction("or",           _or);
    InsertFunction("sentence",     _sentence);
    InsertFunction("thing",        _thing);
    InsertFunction("run",          _run);
    InsertFunction("word",         _word);

    InsertProcedure("bye",         _bye);
    InsertProcedure("for",         _for);
    InsertProcedure("help",        _help);
    InsertProcedure("make",        _make);
    InsertProcedure("print",       _print);
    InsertProcedure("repeat",      _repeat);
    InsertProcedure("setitem",     _setitem);
    InsertProcedure("show",        _show);
    InsertProcedure("to",          _to);

    InsertOperator("+",  PRECEDENCE_ADDOP,          TYPE_ADDOP,          _add);
    InsertOperator("-",  PRECEDENCE_ADDOP,          TYPE_ADDOP,          _sub);
    InsertOperator("*",  PRECEDENCE_MULOP,          TYPE_MULOP,          _mul);
    InsertOperator("/",  PRECEDENCE_MULOP,          TYPE_MULOP,          _div);
    InsertOperator("=",  PRECEDENCE_EQUAL_COMPOP,   TYPE_EQUAL_COMPOP,   _equal);
    InsertOperator("<>", PRECEDENCE_EQUAL_COMPOP,   TYPE_EQUAL_COMPOP,   _not_equal);
    InsertOperator(">",  PRECEDENCE_INEQUAL_COMPOP, TYPE_INEQUAL_COMPOP, _above);
    InsertOperator("<",  PRECEDENCE_INEQUAL_COMPOP, TYPE_INEQUAL_COMPOP, _below);
    InsertOperator(">=", PRECEDENCE_INEQUAL_COMPOP, TYPE_INEQUAL_COMPOP, _above_or_equal);
    InsertOperator("<=", PRECEDENCE_INEQUAL_COMPOP, TYPE_INEQUAL_COMPOP, _below_or_equal);

    return;
}


